#pragma once
#ifndef __LOG_HPP__
#define __LOG_HPP__
#include<iostream>
#include<string>
#include<filesystem>
#include<sys/types.h>
#include<unistd.h>
#include<fstream>
#include"Mutex.hpp"

namespace LogMod{

    using namespace MutexMod;

    class Logstrategy{
        public:
        ~Logstrategy() = default;
        virtual void strategy(const std::string& message) = 0;
    };

    class LogScreen:public Logstrategy
    {
        public:
        LogScreen(){}
        void strategy(const std::string& message)
        {
            _mutex.lock();
            std::cout<<message<<std::endl;

            _mutex.unlock();
        }
        ~LogScreen(){}
        private:
        Mutex _mutex;
    };
    const std::string defpath = "./log/";
    const std::string defname = "mylog.log";

    class Logfile:public Logstrategy
    {
        public:
        Logfile(const std::string path = defpath, const std::string name = defname)
        :_path(path),
        _name(name)
        {
            _mutex.lock();

            if(std::filesystem::exists(_path))
            {
                _mutex.unlock();
                return;
            }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch(const std::filesystem::filesystem_error& e)
            {
                std::cerr << e.what() << '\n';
            }
            _mutex.unlock();

        }

        void strategy(const std::string& message)
        {
            _mutex.lock();
            std::string file = _path + _name;
            std::ofstream out(file.c_str(),std::ios::app);
            if(!out.is_open())
            {
                std::cout<<"NOOPEN"<<std::endl;
                _mutex.unlock();
                return;
            }
            out<<message<<"\n";
            out.close();
            _mutex.unlock();
        }

        void chpath()
        {
            char path[30];
            if(fgets( path,sizeof(path),stdin)){}
            _path =  path;
        }

        void chfname()
        {
            char name[30];
            if(fgets(name,sizeof(name),stdin)){}
            _name = name;
        }

        ~Logfile(){}
        private:
        std::string _path;
        std::string _name;
        Mutex _mutex;
    };

    enum class level{
        DEBUG,
        INFO,
        WARING,
        ERROR,
        FATAL
    };
    std::string GetLevel(level level){
        switch (level)
        {
            case level::DEBUG:
                return "DEBUG";
            case level::INFO:
                return "INFO";
            case level::WARING:
                return "WARING";
            case level::ERROR:
                return "ERROR";
            case level::FATAL:
                return "FATAL";
            default:
                return "UNKONW";
        }
    }

    std::string Getcurtime(){
        time_t tm = time(nullptr);
        struct tm sttm;
        localtime_r(&tm,&sttm);
        char buf[128];
        snprintf(buf,sizeof(buf),"%04d-%02d-%02d %02d:%02d:%02d",
                sttm.tm_year+1900,sttm.tm_mon+1,sttm.tm_mday,
                sttm.tm_hour,sttm.tm_min,sttm.tm_sec);
        return buf;
    }

    class logger{
        public:
        logger(){
            BeLogScreen();
        }
        ~logger(){}

        void BeLogScreen()
        {
            _unilog = std::make_unique<LogScreen>();
        }
        void BeLogfile()
        {
            _unilog = std::make_unique<Logfile>();
        }

        class LogMessage{
            public:
                LogMessage(level level,const std::string& name,const int& line,logger& logger)
                :_time(Getcurtime()),
                _level(GetLevel(level)),
                _pid(getpid()),
                _name(name),
                _line(line),
                _logger(logger)
                {   
                        std::stringstream ssbuff;
                        ssbuff << "[" << _time <<"]" 
                        << "[" << _level << "]"
                        << "[" << _pid << "]"
                        << "[" << _name << "]"
                        << "[" << _line << "]" << "- ";
                        _message = ssbuff.str();
                }   

                template<class T>
                LogMessage& operator<<(const T& Message){
                    std::stringstream ssbuf;
                    ssbuf << Message;
                    _message += ssbuf.str();
                    return *this;
                }

                ~LogMessage(){
                    if(_logger._unilog)
                    {
                        _logger._unilog->strategy(_message);
                    }
                }

            private:
                std::string _time;
                std::string _level;
                pid_t _pid;
                std::string _name;
                int _line;
                std::string _message;
                logger& _logger;
        }; 

        LogMessage operator()(level level,const std::string& name,const int& line){
            return LogMessage(level,name,line,*this);
        }

        private:
            std::unique_ptr<Logstrategy> _unilog;
           // friend class LogMessage;
    };

    logger log;

    #define LOG(level) log(level,__FILE__,__LINE__)
    #define Be_Log_Screen() log.BeLogScreen()
    #define Be_Log_file() log.BeLogfile()
}

#endif